<!doctype html>
<html>
<head>
  <meta charset='utf-8'>
  <title>Backbone.js - Handsontable</title>

  <!--
  Loading Handsontable (full distribution that includes all dependencies)
  -->
  <script data-jsfiddle="common" src="../dist/handsontable.full.js"></script>
  <link data-jsfiddle="common" rel="stylesheet" media="screen" href="../dist/handsontable.full.css">

  <!--
  Loading demo dependencies. They are used here only to enhance the examples on this page
  -->
  <link data-jsfiddle="common" rel="stylesheet" media="screen" href="css/samples.css?20140331">
  <script src="js/samples.js"></script>
  <script data-jsfiddle="common" src="js/backbone/lodash.underscore.js"></script>
  <script data-jsfiddle="common" src="js/backbone/backbone.js"></script>
  <script data-jsfiddle="common" src="js/backbone/backbone-relational/backbone-relational.js"></script>
  <script src="js/highlight/highlight.pack.js"></script>
  <link rel="stylesheet" media="screen" href="js/highlight/styles/github.css">
  <link rel="stylesheet" href="css/font-awesome/css/font-awesome.min.css">

  <!--
  Facebook open graph. Don't copy this to your project :)
  -->
  <meta property="og:title" content="Integrate with Backbone.js">
  <meta property="og:description"
        content="Bind your Backbone collections to Handsontable">
  <meta property="og:url" content="http://handsontable.com/demo/backbone.html">
  <meta property="og:image" content="http://handsontable.com/demo/image/og-image.png">
  <meta property="og:image:type" content="image/png">
  <meta property="og:image:width" content="409">
  <meta property="og:image:height" content="164">
  <link rel="canonical" href="http://handsontable.com/demo/backbone.html">

  <!--
  Google Analytics for GitHub Page. Don't copy this to your project :)
  -->
  <script src="js/ga.js"></script>
</head>

<body>


<div class="wrapper">
  <div class="wrapper-row">
    <div id="global-menu-clone">
      <h1><a href="../index.html">Handsontable</a></h1>

    </div>

    <div id="container">
      <div class="columnLayout">

        <div class="rowLayout">
      <div class="descLayout">
        <div class="pad" data-jsfiddle="example1">
          <h2>Backbone.js</h2>

          <p>
            <a href="http://backbonejs.org/">Backbone.js</a> is a client-side
            MV* framework that can do some pretty smart things
            with data going to and coming back from a server, and has a great
            event model for keeping multiple views in sync.
          </p>

          <p>
            This little example shows how Backbone Models and Collections can
            work with Handsontable. Below, you'll see events firing from
            changes in the <code>CarCollection</code> by Handsontable or
            otherwise.
          </p>

          <div id="example1"></div>

          <p>
            <button name="dump" data-instance="hot" data-dump="#example1" title="Prints current data source to Firebug/Chrome Dev Tools">
              Dump data to console
            </button>
            <button id="add_car">Simulate a new Car</button>

            <br>
            <select multiple="multiple" id="example1_events" style="width: 100%; height: 100px"></select>
          </p>

          <p>
            <strong>Please note</strong> that Backbone integration is a work in progress since Handsontable 0.8.14.
            The code presented here has 2 known issues:
          </p>

          <ul>
            <li>inserting and removing rows or columns triggers errors, both when using <code>alter</code> method and
              the context menu
            </li>
            <li><code>minSpareRows</code> does not have effect directly after row was added from Backbone (as a
              workaround, you would need to call <code>loadData</code> instead of <code>render</code>)
            </li>
          </ul>

          <p>
            Both issues will be addressed in future versions of HT. Contributions are welcome!
          </p>
        </div>
      </div>

      <div class="codeLayout">
        <div class="pad">
          <div class="jsFiddle">
            <button class="jsFiddleLink" data-runfiddle="example1">Edit in jsFiddle</button>
          </div>

          <script data-jsfiddle="example1">
            var
              container = document.getElementById('example1'),
              addCar = document.getElementById('add_car'),
              eventHolder = document.getElementById('example1_events'),
              CarModel = Backbone.Model.extend({}),
              CarCollection,
              cars,
              hot;

            CarCollection = Backbone.Collection.extend({
              model: CarModel,
              // Backbone.Collection doesn't support `splice`, yet! Easy to add.
              splice: hackedSplice
            });

            cars = new CarCollection();

            // since we're not using a server... make up some data. This will make
            // a couple CarModels from these plain old objects
            cars.add([
              {make: 'Dodge', model: 'Ram', year: 2012, weight: 6811},
              {make: 'Toyota', model: 'Camry', year: 2012, weight: 3190},
              {make: 'Smart', model: 'Fortwo', year: 2012, weight: 1808}
            ]);

            hot = new Handsontable(container, {
              data: cars,
              dataSchema: makeCar,
              contextMenu: true,
              columns: [
                attr('make'),
                attr('model'),
                attr('year')
              ],
              colHeaders: ['Make', 'Model', 'Year']
              // minSpareRows: 1 //see notes on the left for `minSpareRows`
            });

            // this will log all the Backbone events getting fired!
            cars.on('all', logEvents)
              .on('add', function () {
                hot.render();
              })
              .on('remove', function () {
                hot.render();
              });

            // you'll have to make something like these until there is a better
            // way to use the string notation, i.e. "bb:make"!

            // normally, you'd get these from the server with .fetch()
            function attr(attr) {
              // this lets us remember `attr` for when when it is get/set
              return {data: function (car, value) {
                if (_.isUndefined(value)) {
                  return car.get(attr);
                }
                car.set(attr, value);
              }};
            }

            // just setting `dataSchema: CarModel` would be great, but it is non-
            // trivial to detect constructors...
            function makeCar() {
              return new CarModel();
            }

            // use the "good" Collection methods to emulate Array.splice
            function hackedSplice(index, howMany /* model1, ... modelN */) {
              var args = _.toArray(arguments).slice(2).concat({at: index}),
                removed = this.models.slice(index, index + howMany);

              this.remove(removed).add.apply(this, args);

              return removed;
            }

            // show a log of events getting fired
            function logEvents(event, model) {
              var now = new Date(),
                option = document.createElement('OPTION');

              option.innerHTML = [':', now.getSeconds(), ':', now.getMilliseconds(), '[' + event + ']',
                JSON.stringify(model)].join(' ');
              eventHolder.insertBefore(option, eventHolder.firstChild);
            }

            Handsontable.Dom.addEvent(addCar, 'click', function () {
              cars.add({make: "Tesla", model: "S", year: 2012, weight: 4647.3});
            });
          </script>
        </div>
      </div>
    </div>

        <div class="footer-text">Handsontable &copy; 2012-2014 Marcin Warpechowski and contributors.<br> Code and documentation
          licensed under the The MIT License.
        </div>
      </div>

    </div>

  </div>
</div>

<div id="outside-links-wrapper"></div>

</body>
</html>
